More on Digitalmars D - Simple SVG render for GnomeCanvas
Published 2006-02-28 09:50:42
As usual, the best way to learn a language is to try and write something in it, so for a bit of fun I had a go at writing a SVG renderer (or at least a very simple one). In doing so, I got to see how D can be used to call external libraries, and explore some of the existing libraries and syntax that was available.
To render to the screen I decided to use GnomeCanvas, As I had played with it before in C, and from what I remember even tried writing bindings for PHP-GTK a long time ago. So I had some idea of how the API worked.
.. click on the more link for the full article ...
Making the API usable
First on the tasks was to make the API available to D, This is done by copying and manipulating the header file into a extern(C) block,For example:
GtkWidget * gnome_canvas_new_aa(void);becomes
extern(C)
{
GtkWidget* function() gnome_canvas_new_aa;
}
enabling you to call the function in D using
GtkWidget *canvas;Since GtkWidget is defined in the DUI package, I had to add a few imports:
canvas = gnome_canvas_new_aa();
private import dui.DUI;DUI provides wrappers to a D style interface for GTK, however, to make things easier to start with, I used the native C methods to call gtk, and mixed them up a bit with the gtk object wrapper, so creating a new window became:
private import def.Types;
private import def.Constants;
private import lib.gtk;
GtkWidget *app;
app = gtk_window_new (GtkWindowType.TOPLEVEL);
gtk_window_fullscreen( cast(GtkWindow*) app );
Compiling this little baby
The D compilation is quite simple:/dmd/bin/dmd -gc -c -O -od. canvas.dHowever, when it came to linking I took me a while to understand why linking it to the gnomecanvas lib's caused the application to segfault on the first gtk call.
-I/dmd/src/phobos:/usr/src/dyndui/src:/usr/src/dool/src/
gcc canvas.o -o canvas -L/usr/src/dool -ldool -L/usr/src/dyndui -lduiWhat I failed to understand until I dug into the DUI code, was that libgtk was being dynamically loaded and linked by DUI. so linking it here caused the library to be doublely linked, and hence segfault.
-lphobos -lstdc++ -ldl -pthread -L/usr/src/dool -ldool -lphobos
`pkg-config --libs libgnomecanvas-2.0`
The correct compilation line does not need the pkg-config, eg.
gcc canvas.o -o canvas -L/usr/src/dool -ldool -L/usr/src/dyndui -ldui
-lphobos -lstdc++ -ldl -pthread -L/usr/src/dool -ldool -lphobos
Dynamic Linking
The answer was to duplicate what was being done by DUI, and use the dui linker code. There where 2 parts to this- Create a list of symbols that are used
- Load the dynamic library using dl()
Adding the symbol list is quite easy, first define an array of symbols to send to the linker.
Symbol[] GnomeCanvasLinks = [
{ "gnome_canvas_new_aa", cast(void**)& gnome_canvas_new_aa } ,
.....
];
Then define a constructor (and destructor) for the file:
static this()
{
gnomecanvas_Linker = new Linker( "libgnomecanvas-2.so" );
gnomecanvas_Linker.link( GnomeCanvasLinks );
}
static ~this()
{
delete gnomecanvas_Linker;
}
Then define a constructor (and destructor) for the file:
Using setters to create nice interfaces
One of the key components of canvas is drawing things, this is done using the gnome_canvas_item_new() call.gnome_canvas_item_new(GnomeCanvasGroup* parent,
GType type, gchar* first_arg_name,...)
gnome_canvas_item_new(root, gnome_cavnas_rect_get_type(),
"x1", 12.0,
"y1", 12.0, ........etc....
This is a function call that uses varargs, and sends a variety of types down the line to the constructor. Unfortunatly although D will generally autotypecast data to normal function calls, it is asking a bit much for it to guess what random data you may be sending to this varargs function.
To get around this, I eventually worked out that sending null as first_arg_name, and the first argument could be used to create the object.eg.
this.item = gnome_canvas_item_new ( parent,type,null,null);
After that, using the gnome_canvas_item_set, and correctly casting the data to the correct type can be used to set the values:
gnome_canvas_item_set(this.item, cast(char*) "x1", (cast(double) 123.0);Since D is a wonderfull hybrid of functional and object orientated language, I decided to try out an object to represent this.
class CanvasItem {Set's up the constructor wrapper around the CanvasItem.
GnomeCanvasItem* item;
this(GnomeCanvasGroup* parent, GType type)
{
this.item = gnome_canvas_item_new ( parent,type,null,null);
}
now by overloading the set() method, I can send it various combinations of types to set the coordinates and text etc.
void set(char[] k, char[] v)
{
gnome_canvas_item_set(this.item, cast(char *)k, cast(char *)v,null);
}
void set(char[] k, double v)
{
gnome_canvas_item_set(this.item, cast(char *)k, v,null);
}
void set(char[] k, int v)
{
gnome_canvas_item_set(this.item, cast(char *)k, v,null);
}
However, using this method I would still have to keep calling
canvas_item.set("x1",123.0);
and make sure it was sending the right type for each attribute, so I had a go at using D's setter syntax, by defining a method with the same name as the property you intend to make available, you can make it perform a set operation.
void x1(double v) { this.set("x1", v); }
void x2(double v) { this.set("x2", v); }
Meaning that you can now write:
canvas_item = new CanvasItem(parent);
canvas_item.x1 = 123;
canvas_item.y1 = 456;
This follows through to setting colours and fonts
void font(char[] v) { this.set("font", v); }I even got clever allowing fill_color_rgba to accept "#ffccff" string style values, rather than 0xffccff00, by overloading the setter to accept string types:
void fillColor(char[] v) { this.set("fill-color", v); }
}
void fill_color_rgba(double v)
{
this.set("fill_color_rgba", cast(int) v);
}
void fill_color_rgba(char[] v)
{
if (v == "none") {
return;
}
int c=0;
int mult = 0x10000000;
for (int i = 1; i < 7; i++) {
int p = ifind(hexdigits, v[i]);
c += (p > -1) ? mult * p : 0;
mult = mult / 0x10;
}
c+=0xff;
this.set("fill_color_rgba", cast(int) c);
}
I'm sure given more time, that could be even cleaner :)
XML Libraries
One of the things that is so beautiful about D, is that the standard library, phobos, is so small and compact, which makes it very easy to learn, and quickly use.
XML parsing is not included in the core library (thankfully), so I started looking around the D wiki, and found a number of XML libraries listed there. All come with source, and are at various stages of development, (although most can parse and represent XML in some manner or other).
The libraries are also distributed as tarballs usually, often with little in the way of compilation or installation notes. (although given some common sense it's not too dificult to work out). It seems there is a slight gap in the community that could be easily filled to provide a standard installer (like pear), or just make them available as .deb's.
I also found that a number of the larger projects on dsource had written or adapted xml parsers, DOM representations and bundled them with the source of them. eg. mango and DUI(well dool acutally) both contain XML parsers.
For my tiny example program i settled on using the xml library that came with DUI/dool, as it was already installed and did not take much to learn. Unfortunatly I quickly found that it was a touch incomplete as it failed to parse XML comments. But for my simple test program, removing the comments from the SVG file was a simple solution.
to use the library you just have to import the namespace (or you could just use the full path in your code.)
private import dool.xml.xml;or
XmlParser x = new XmlParser();
XmlDocument xmldoc = x.parse(testdata);
auto x = new dool.xml.xml.XmlParser();should work (well I havent tested the second, but it is a little more readable, as the import line ends up at the top of the file.
auto xmldoc = x.parse(testdata);
the xmldoc.children, than then be recursively looped through and used to call the CanvasItem method to add items to the canvas.
File IO
In the simple test example, I also read the XML data from a file, most of the tutorials for D used readLine, however I wanted to read the whole file into a string so after a bit of digging I wrote this.File f = new File( r"/tmp/data.svg", FileMode.In );almost as simple as file_get_contents(), and since D cleans up the File handle at the end of the method it's in, you dont really need to delete f to close the file handle. [slight correction here thanks to Regan Heath] the destructor will not be called unless f is defined as type auto, and there is another method read(), which can read the whole file in one go.
while (!f.eof()) {
testdata ~= f.readString( f.available());
}
char[] testdata = cast(char[]) std.file.read( r"/tmp/data.svg");
String parsing
One of the key parts of parsing a text file (SVG) to create graphical elements, that require integers and floats is how easy it is to work with. D uses standard C calls to convert types, so reading the x and Y coordinates is very simple:CanvasItem ci = new CanvasItem(parent,gnome_canvas_text_get_type());atof and atoi provide the conversion, and since D supports string based switch case, looping through the style component of the SVG file is quite simple. as shown here.
ci.x = atof(node.getAttribute("x").value);
ci.y = atof(node.getAttribute("y").value);
void setStyle(CanvasItem ci)Well, my little SVG renderer can just render rectangles and text at present, but it's interesting to see how simple everything comes together, and how a language designed properly can really make a huge difference.
{
char[][] bits = split(this.currentStyle,";");
foreach(int i, char[] kv; bits) {
if (-1 == find(kv,":")) {
continue;
}
char[][] kv_ar = split(kv,":");
char [] k = kv_ar[0];
char [] v = kv_ar[1];
printf("set: %.*s=> %.*s\n", k,v);
switch(k) {
case "font-family": ci.font=v; break;
case "font-size": ci.size = atoi(replace(v,"px","")); break;
case "font-weight":
if (v == "bold") {
ci.weight = 800;
}
break;
case "fill": ci.fill_color_rgba = v; break;
default: break;
}
}
}
google.com : february (101 referals)
google.com : php atoi (96 referals)
www.planet-php.net : Planet PHP (53 referals)
google.com : atoi php (44 referals)
google.com : php varargs (31 referals)
www.digitalmars.com : Two little blog/tutorials posts. (21 referals)
planet.debian.org.hk : Debian HK : Debian @ Hong Kong (13 referals)
google.com : digital mars d (12 referals)
google.com : varargs php (12 referals)
google.com : atoi in php (10 referals)
google.com : december (10 referals)
google.com : digitalmars (10 referals)
www.digitalmars.com : Digital Mars - digitalmars.D.learn - Two little blog/tutorials posts. (8 referals)
google.com : php svg render (8 referals)
google.com : php svg renderer (8 referals)
google.com : svg renderer (7 referals)
google.com : (6 referals)
google.com : digitalmars d (6 referals)
dstress.kuehne.cn : dstress.kuehne.cn: links (6 referals)
www.phpn.org : More on Digitalmars D - Simple SVG render for GnomeCanvas (5 referals)
Follow us
-
- Some thoughts on the language server and its usefulness in the roobuilder
- Roo Builder for Gtk4 moving forward
- Clustered Web Applications - Mysql and File replication
- GitLive - Branching - Merging
- PDO_DataObject Released
- PDO_DataObject is under way
- Mass email Marketing and anti-spam - some of the how-to..
- Hydra - Recruitment done right
Blog Latest
-
Twitter - @Roojs